package cn.akwangl.concurrency.web.common.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Redis 分布式锁.
 *
 * @author 阿坤同学
 * @date 2022/11/26 21:59
 */
@Slf4j
public class RedisLock {

    private RedisTemplate redisTemplate;

    public RedisLock(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取分布式锁
     *
     * @param key            redisKey
     * @param value          redisValue
     * @param expirationTime key 过期时间
     * @param timeUnit       过期时间单位
     * @return true:成功获取锁 false:获取锁失败
     * @author 阿坤同学
     * @date 2022/11/26 22:18
     */
    public boolean getLock(String key, String value, long expirationTime, TimeUnit timeUnit) {
        byte[] keyByteArr = redisTemplate.getKeySerializer().serialize(key);
        byte[] valueByteArr = redisTemplate.getValueSerializer().serialize(value);
        Expiration expiration = Expiration.from(expirationTime, timeUnit);
        RedisStringCommands.SetOption option = RedisStringCommands.SetOption.ifAbsent();
        RedisCallback<Boolean> action = connection -> connection.set(keyByteArr, valueByteArr, expiration, option);
        return (boolean) redisTemplate.<Boolean>execute(action);
    }

    /**
     * 解锁.
     *
     * @param key   redisKey
     * @param value redisValue
     * @return true:成功 false:失败
     * @author 阿坤同学
     * @date 2022/11/26 22:17
     */
    public void unlock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class);
        List<String> keys = Arrays.asList(key);
        Object unlockResult = redisTemplate.<Boolean>execute(redisScript, keys, value);
        log.debug("redis 移除业务%{}锁返回结果:%s", key, unlockResult);
    }
}
